﻿ using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.Specialized;
 using System.IO;
 using System.Linq;
 using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Documents;
 using System.Windows.Media;
 using System.Windows.Threading;
 using GalaSoft.MvvmLight.Command;
 using GalaSoft.MvvmLight.Messaging;
 using Microsoft.Win32;
 using ProjectWizardManagement.Entity.Model;
 using ProjectWizardManagementv1.Infrastructure.Abstract;
 using ProjectWizardManagementv1.Infrastructure.Concrete;
 using ProjectWizardManagementv1.Views;
 using ProjectWizardManagementv1.WpfComponents.ComponentClasses;
 using ProjectWizardManagementv1.WpfComponents.UserControls;

namespace ProjectWizardManagementv1.ViewModels.Concrete
{
    public class WorkspaceViewModel : ProjectViewModelBase
    {
        #region Members
        private Project _project;
        private readonly OpenFileDialog _addFileDialog;
        private FileExplorerViewModel _files;
        private readonly FileSystemWatcher _fileSystemWatcher;
        private readonly bool _allowOverwrite;
        #endregion

        public override void Cleanup()
        {
            _project = null;
            _files = null; 
            base.Cleanup();
        }

        #region Properties
        public ObservableCollection<ProjectFileViewModel> Files
        {
            get { return _files.RootFiles; }
        }

        public ObservableCollection<ProjectFileViewModel> FilesAsList
        {
            get { return _files.GetAllFilesAsList(); }
        }

        public TasksControlViewModel Tasks { get; set; }

        public FileExplorerViewModel FileExplorer
        {
            get { return _files; }
            set { _files = value;
                Messenger.Default.Send(_files.GetAllFilesAsList());/*RaisePropertyChanged("ProjectExplorer");*/ RaisePropertyChanged("Files"); }
        }

        public Project Project
        {
            get { return _project; } 
            set 
            { 
                _project = value;
                Messenger.Default.Send(Project.Tasks);
                Messenger.Default.Send(Project.CalendarEvents);
                _fileSystemWatcher.Path = value.ProjectDirectory;
                _fileSystemWatcher.EnableRaisingEvents = true;
                _fileSystemWatcher.IncludeSubdirectories = true;
                RaisePropertyChanged("Project"); 
            }
        }

        public IFileSystem FileSystem { get; set; }

        private List<ProjectFileViewModel> SelectedFiles { get; set; }
        #endregion

        #region Commands

        public RelayCommand AddFile { get; private set; }
        public RelayCommand CopyFile { get; private set; }
        public RelayCommand PasteFile { get; private set; }
        public RelayCommand DeleteFile { get; private set; }
        public RelayCommand RenameFile { get; private set; }
        public RelayCommand ViewFile { get; private set; }
        public RelayCommand OpenFileOutside { get; private set; }
        public RelayCommand CloseWorkspace { get; private set; }
        public RelayCommand EditDocument { get; private set; }
        #endregion

        #region Constructors
        public WorkspaceViewModel()
        {
            FileSystem = new ComputerFileSystem();
            _files = new FileExplorerViewModel();
            SelectedFiles = new List<ProjectFileViewModel>();
            Messenger.Default.Register<Project>(this, project => Project = project);
            Messenger.Default.Register<ProjectFile[]>(this, f => FileExplorer = new FileExplorerViewModel(f.ToList(), Project.ProjectDirectory));
            Messenger.Default.Register<List<ProjectFileViewModel>>(this, selected => SelectedFiles = selected);
            Messenger.Default.Register<RenameFileMessage>(this, this.RenameFileExecution);
            _allowOverwrite = true;
            _fileSystemWatcher = new FileSystemWatcher();
            _addFileDialog = new OpenFileDialog
                             {                        
                                 Filter = "All Files (*.*)|*.*",
                                 Title = "Add File to Project"
                             };
            this.CreateCommands();
            this.AddWatchers();
        }

        #region Constructors ComponentBuilders
        private void CreateCommands()
        {
            AddFile = new RelayCommand(AddFileExecution);
            CopyFile = new RelayCommand(CopyFileExecution, HasAtleastOneSelectedFile);
            PasteFile = new RelayCommand(PasteFileExecution, Clipboard.ContainsFileDropList);
            DeleteFile = new RelayCommand(DeleteFileExecution,() => HasAtleastOneSelectedFile() && !SelectedIsOpen());
            OpenFileOutside = new RelayCommand(OpenFileOutsideExecution, () => HasAtleastOneSelectedFile() && !SelectedHasDirectory() && !SelectedIsOpen());
            CloseWorkspace = new RelayCommand(CloseWorkspaceExecution);
            RenameFile = new RelayCommand(RenameFileExecution, () => HasAtleastOneSelectedFile() && !HasMultipleSelectedFiles() && !SelectedIsOpen());
            ViewFile = new RelayCommand(ViewFileExecution, () => SelectedAreViewable() && !SelectedIsOpen());
            EditDocument = new RelayCommand(EditDocumentExecution,() => HasAtleastOneSelectedFile() && !SelectedIsOpen());
        }
                                                                                                                               
        private void AddWatchers()
        {
            _fileSystemWatcher.Created += OnFileCreated;
            _fileSystemWatcher.Deleted += OnFileDeleted;
            _fileSystemWatcher.Renamed += OnFileRenamed;
        }
        #endregion
        #endregion

        #region Command Executions
        public void AddFileExecution()
        {
            if (_addFileDialog.ShowDialog().HasValue)
            {
                FileSystem.FileCopy(_addFileDialog.FileName, _project.ProjectDirectory + Path.GetFileName(_addFileDialog.FileName),
                          _allowOverwrite);
            }
        }

        public void CopyFileExecution()
        {
            Clipboard.Clear();

            var newSelectedFiles = FileExplorer.IgnoreChildrenOfSelectedParents(SelectedFiles);

            var files = new StringCollection();
            foreach (var file in newSelectedFiles)
                files.Add(file.Path);

            Clipboard.SetFileDropList(files);
        }

        public void PasteFileExecution()
        {
            if(Clipboard.ContainsFileDropList())
            {
                
                foreach (string file in Clipboard.GetFileDropList())
                {
                    if(FileSystem.FileExists(file))
                        FileSystem.FileCopy(file, _project.ProjectDirectory + Path.GetFileName(file), _allowOverwrite);
                }
            }
        }

        public void DeleteFileExecution()
        {
            List<ProjectFileViewModel> newSelectedList = FileExplorer.IgnoreChildrenOfSelectedParents(SelectedFiles);

            foreach(var selectedFile in newSelectedList)
            {
                if(File.Exists(selectedFile.Path) || Directory.Exists(selectedFile.Path))
                {
                    var result = MessageBox.Show(string.Format("'{0}' will be deleted permenently.\nAre you sure you want to delete?", selectedFile.Name), "File Delete",
                                    MessageBoxButton.YesNo, MessageBoxImage.Information
                        );

                    try
                    {
                        if(result == MessageBoxResult.Yes && !selectedFile.IsDirectory)
                            FileSystem.FileDelete(selectedFile.Path);
                        else if(result == MessageBoxResult.Yes && selectedFile.IsDirectory)
                            FileSystem.DirectoryDelete(selectedFile.Path);
                    }
                    catch(IOException)
                    {
                        MessageBox.Show(string.Format("{0} is being used by another process.", selectedFile.FileName),
                                        "File Deletion Failed",
                                        MessageBoxButton.OK, MessageBoxImage.Exclamation);
                    }
                    
                } 
            }

        }

        public void ViewFileExecution()
        {

            foreach (var file in SelectedFiles)
            {
                var tab = new ClosableTab();
                var docView = new AdvancedDocumentViewer();
                var xpsDoc = OfficeFileViewer.ViewDocument(file, _project.ProjectDirectory);

                if (xpsDoc != null)
                {
                    docView.OpenedFile = file;
                    docView.ViewFile = xpsDoc;
                    tab.ClosableContent = docView;
                    tab.Title = file.Name + " (View)";
                    Messenger.Default.Send(tab);
                }
                else
                    MessageBox.Show("The file cannot be viewed.", "File View Error", MessageBoxButton.OK, MessageBoxImage.Exclamation);
            }

        }

        public void EditDocumentExecution()
        {
            
            foreach (var file in SelectedFiles)
            {
                try
                {
                    if (file.Extension == ".docx")
                        ReadWordDocument(file);
                    else if (file.Extension == ".xlsx")
                        ReadExcelDocument(file);
                    else
                        ReadPlainTextFile(file);
                }
                catch (IOException)
                {
                    MessageBox.Show(
                        "The file is being used by another process. Please close the other process and try again.",
                        "Failed to Open",
                        MessageBoxButton.OK, MessageBoxImage.Exclamation);
                }
            }
        }

        private void ReadPlainTextFile(ProjectFileViewModel file)
        {
            using(var reader = new StreamReader(file.Path))
            {
                string line;
                var textbox = new PlainTextEditor {DestinationPath = file.Path};
                while ((line = reader.ReadLine()) != null)
                {
                    textbox.Body.Text += line + "\n";
                }

                var tab = new ClosableTab {Content = textbox};
                Messenger.Default.Send(tab);
            }
        }

        private void ReadWordDocument(ProjectFileViewModel file)
        {
            var parser = new WordDocxDocumentParser();
            var wordDocument = WordDocxDocumentIO.ReadDocument(file.Path);
                var tab = new ClosableTab();
            FlowDocument document = parser.ParseDocument(wordDocument);
            var textbox = new AdvanceRichTextEditor {Body = {Document = document}, DestinationPath = file.Path};
            tab.Content = textbox;
            Messenger.Default.Send(tab);
        }

        private void ReadExcelDocument(ProjectFileViewModel file)
        {
            var parser = new ExcelDocumentParser();
            var grid = parser.ParseDocument(file.Path);
            var tab = new ClosableTab();
            var gridPanel = new Grid();
            var scrollView = new ScrollViewer{VerticalScrollBarVisibility = ScrollBarVisibility.Visible, HorizontalScrollBarVisibility = ScrollBarVisibility.Visible};
            for (int i = 0; i < grid.GetUpperBound(0); i++)
                gridPanel.RowDefinitions.Add(new RowDefinition {Height = new GridLength(25)});
            for(int i = 0; i< grid.GetUpperBound(1); i++) 
                gridPanel.ColumnDefinitions.Add(new ColumnDefinition {Width = new GridLength(100)});

            for (int i = 0; i < grid.GetUpperBound(0); i++ )
            {
                for(int j = 0; j < grid.GetUpperBound(1); j++)
                {
                    grid[i,j].SetValue(Grid.RowProperty, i);
                    grid[i, j].SetValue(Grid.ColumnProperty, j);
                    gridPanel.Children.Add(grid[i, j]);
                }
                    
            }
            scrollView.Content = gridPanel;
            var excelEditor = new ExcelEditor(grid) {DestinationPath = file.Path};
            excelEditor.ExcelArea.Children.Add(scrollView);
                tab.Content = excelEditor;
            Messenger.Default.Send(tab);

        }
                                                                                       
        public void OpenFileOutsideExecution()
        {
            foreach (var file in SelectedFiles)
            {
                System.Diagnostics.Process.Start(file.Path); 
            }
        }

        public void CloseWorkspaceExecution()
        {
            new XmlIO().SerializeToOutput(string.Format(@"{0}{1}.projwm", Project.ProjectDirectory, Project.Name), Project);
            this.Cleanup();
            Messenger.Default.Send(ViewModelKey.ProjectDialog);
            
        }

        public void RenameFileExecution()
        {
            var file = SelectedFiles.First(p => true);
            var window = new RenameFileWindow(file);
            window.ShowDialog();
        }

        private void RenameFileExecution(RenameFileMessage rename)
        {
            if(rename.File.IsDirectory)
                FileSystem.DirectoryMove(rename.OldPath, rename.NewPath);
            else
                FileSystem.FileMove(rename.OldPath, rename.NewPath);
        }

        #endregion

        #region Command Checks
        private bool HasAtleastOneSelectedFile()
        {
            return SelectedFiles.Count >= 1;
        }

        private bool HasMultipleSelectedFiles()
        {
            return SelectedFiles.Count > 1;
        }

        private bool SelectedHasDirectory()
        {
            return SelectedFiles.Select(f => f.IsDirectory).Contains(true);
        }

        private bool SelectedAreViewable()
        {
            return SelectedFiles.All(f => OfficeFileViewer.ConversionExtensionDictionary.ContainsKey(f.Extension));
        }

        private bool SelectedIsOpen()
        {
            return SelectedFiles.Any(f => f.IsOpen);
        }

        #endregion

        #region Methods

        #endregion

        #region FileSystemWatcher Methods
        private void OnFileCreated(object sender, FileSystemEventArgs e)
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send,new Action( () =>
                                                                {
                                                                        var file = FileSystemFactory.ConstructFileHierarchy(e.FullPath);
                                                                        _files.AddFileToParentDirectory(file);
                                                                        RaisePropertyChanged("Files"); 
                                                                }));
        }

        private void OnFileDeleted(object sender, FileSystemEventArgs e)
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
                                                                {
                                                                        _files.RemoveFileFromParentDirectory(e.FullPath);
                                                                        RaisePropertyChanged("Files");
                                                                }));
        }

        public void OnFileRenamed(object sender, RenamedEventArgs e)
        {
    
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>
                                                                {
                                                                        _files.RenameFile(e.OldFullPath, e.FullPath);
                                                                        RaisePropertyChanged("Files");  
                                                                }));
        }
        #endregion

    }

    #region MessageModels
    public class RenameFileMessage
    {
        public string OldPath { get; private set; }
        public string NewPath { get; private set; }
        public ProjectFileViewModel File { get; private set; }

        public RenameFileMessage(string oldPath, string newPath, ProjectFileViewModel file)
        {
            OldPath = oldPath;
            NewPath = newPath;
            File = file;
        }
    }
    #endregion
}
